from collections import namedtuple

# from .board import Board, GameState
from .gotypes import Player, Point


class Territory:
    def __init__(self, territory_map) -> None:
        self.num_black_territory = 0
        self.num_white_territory = 0
        self.num_black_stones = 0
        self.num_white_stones = 0
        self.num_dame = 0
        self.dame_points = []
        for point, status in territory_map.items():
            if status == Player.black:
                self.num_black_stones += 1
            elif status == Player.white:
                self.num_white_stones += 1
            elif status == "territory_b":
                self.num_black_territory += 1
            elif status == "territory_w":
                self.num_white_territory += 1
            elif status == "dame":
                self.num_dame += 1
                self.dame_points.append(point)


class GameResult(namedtuple("GameResult", "b w komi")):
    @property
    def Winner(self):
        if self.b > self.w + self.komi:
            return Player.black
        return Player.white

    @property
    def WinnerMargin(self):
        w = self.w + self.komi
        return abs(self.b - w)

    def __str__(self) -> str:
        w = self.w + self.komi
        if self.b > w:
            return f"B+{self.b-w:.1f}"
        return f"W+{w-self.b:.1f}"


def ComputeGameResult(game_state):
    territory = EvaluateTerritory(game_state.board)
    return GameResult(territory.num_black_territory + territory.num_black_stones,
                      territory.num_white_territory + territory.num_white_stones,
                      komi=7.5)


def EvaluateTerritory(board):
    status = {}
    for r in range(1, board.rows + 1):
        for c in range(1, board.cols + 1):
            p = Point(r, c)
            if p in status:
                continue
            stone = board.GetPlayer(p)
            if stone is not None:
                status[p] = board.GetPlayer(p)
            else:
                group, neighbors = CollectRegion(p, board)
                if len(neighbors) == 1:
                    neighbor_stone = neighbors.pop()
                    stone_str = "b" if neighbor_stone == Player.black else "w"
                    fill_with = "territory_" + stone_str
                else:
                    fill_with = "dame"
                for pos in group:
                    status[pos] = fill_with
    return Territory(status)


def CollectRegion(point: Point, board, visited=None):
    if visited is None:
        visited = {}
    if point in visited:
        return [], set()
    all_points = [point]
    all_borders = set()
    visited[point] = True
    here = board.GetPlayer(point)
    deltas = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    for dr, dc in deltas:
        next_p = Point(row=point.row + dr, col=point.col + dc)
        if not board.IsOnGrid(next_p):
            continue
        neighbor = board.GetPlayer(next_p)
        if neighbor == here:
            points, borders = CollectRegion(next_p, board, visited)
            all_points += points
            all_borders |= borders
        else:
            all_borders.add(neighbor)
    return all_points, all_borders
